using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using System.Linq;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

namespace ModelContentProcessors
{
    [ContentProcessor]
    public class VertexProcessor : ModelProcessor
    {
        readonly private Dictionary<MeshContent, List<Vector3>> _vertices = new Dictionary<MeshContent, List<Vector3>>();

        public override ModelContent Process(NodeContent input,
                                             ContentProcessorContext context)
        {
            ModelContent model = base.Process(input, context);

            // Look up the input vertex positions.
            FindVertices(input);

            Dictionary<string, object> tagData = new Dictionary<string, object>();
            model.Tag = tagData;

            Dictionary<string, List<Vector3>> verts= _vertices.ToDictionary(k => k.Key.Name, v => v.Value);
            tagData.Add("Vertices", verts);

            BoundingBox boundingBox = ComputeBoundingBox();
            tagData.Add("BoundingBox", boundingBox);
            tagData.Add("SubMeshBoundingBoxes", ComputeSubMeshBoundingBoxes());
            return model;
        }
        private BoundingBox ComputeBoundingBox()
        {
            var transformedVecs =
                _vertices.SelectMany(pair => 
                pair.Value.Select(v => Vector3.Transform(v, pair.Key.AbsoluteTransform)));

            return BoundingBox.CreateFromPoints(transformedVecs);
        }
        private Dictionary<string,BoundingBox> ComputeSubMeshBoundingBoxes()
        {
            var subMeshBoundingBoxes = new Dictionary<string, BoundingBox>();
            foreach (KeyValuePair<MeshContent,List<Vector3>> pair in _vertices)
            {
                BoundingBox meshBoundingBox = BoundingBox.CreateFromPoints(pair.Value);
                subMeshBoundingBoxes.Add(pair.Key.Name,meshBoundingBox);
            }
            return subMeshBoundingBoxes;
        }
        private void FindVertices(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                List<Vector3> verts = new List<Vector3>();
                                
                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    List<Vector3> normals = new List<Vector3>();
                 
                    for (int i = 0; i < geometry.Indices.Count; i++)
                    {
                        int index = geometry.Indices[i];
                        // Look up the position of this vertex.
                        Vector3 vertex = geometry.Vertices.Positions[index];

                        if ((i) % 3 == 0)
                        {
                            Vector3 v1 = geometry.Vertices.Positions[geometry.Indices[i]];
                            Vector3 v2 = geometry.Vertices.Positions[geometry.Indices[i + 1]];
                            Vector3 v3 = geometry.Vertices.Positions[geometry.Indices[i + 2]];

                            Vector3 vu = v3 - v1;
                            Vector3 vt = v2 - v1;
                            Vector3 normal = Vector3.Cross(vu, vt);
                            normals.Add(normal);
                        }                        
                        verts.Add(vertex);
                    }
                    if (geometry.Vertices.Channels.Contains(VertexChannelNames.Normal(0)) == false)
                        geometry.Vertices.Channels.Add(VertexChannelNames.Normal(0), normals);
                }
                _vertices.Add(mesh,verts);                
            }            
            foreach (NodeContent child in node.Children)
            {
                FindVertices(child);
            }
        }
    }
}
